package com.learn.security.config;

import com.learn.security.app.service.impl.CusSessionInformationExpiredStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * @author PC
 * Security配置类
 */
@Configuration
public class SecurityAutoConfiguration {

    private SecurityConfigProperties securityConfigProperties;

    private UserDetailsService userDetailsService;
    /**
     * 注入数据源
     */
    private DataSource dataSource;

    @Autowired
    public void setSecurityConfigProperties(SecurityConfigProperties securityConfigProperties) {
        this.securityConfigProperties = securityConfigProperties;
    }

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    @Lazy
    @Autowired
    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public GrantedAuthorityDefaults grantedAuthorityDefaults(){
        return new GrantedAuthorityDefaults(securityConfigProperties.getRolePrefix());
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                //自定义登录页面
                .formLogin()
                //登陆页面设置
                .loginPage("/login.html")
                //登录url设置
                .loginProcessingUrl("/user/login")
                //登录成功后跳转的路径,如果希望跳回原路径,alwaysUse不填或填false
                .defaultSuccessUrl("/success.html")
                //允许访问
                .permitAll()
                //配置登出
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/login.html").permitAll()
                //未获得授权跳转的页面
                .and().exceptionHandling().accessDeniedPage("/un-auth.html")
                //设置认证权限
                .and().authorizeRequests()
                // /v1/**的没有权限注解的全部放行
                .antMatchers("/v1/**").permitAll()
                .anyRequest().authenticated()
                //自动登录
                .and().rememberMe().tokenRepository(persistentTokenRepository())
                //自动登录超时时间
                .tokenValiditySeconds(24*60*60)
                //设置userDetailService
                .userDetailsService(userDetailsService)
                //关闭csrf防护
                .and().csrf().disable()
                //限定登录数
                .sessionManagement().maximumSessions(securityConfigProperties.getMaximumSessions()).expiredSessionStrategy(new CusSessionInformationExpiredStrategy()).maxSessionsPreventsLogin(false);
        return http.build();
    }

    /**
     * 令牌持久化，配置数据库操作对象
     * 如果想要让程序实现自动建表，可以添加如下配置
     * jdbcTokenRepository.setCreateTableOnStartup(true);
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }

}
